#include "testing.h"

#define info(...) printk("<1>" __VA_ARGS__)

char * flagsToString(u64 flags, char *str) {
  char *s = str;
  
  s[0] = 0;
  
  if (flags & PF_PRESENT) 	s += sprintf(s,  "P ");
  if (flags & PF_WRITE) 	s += sprintf(s,  "W ");
  if (flags & PF_USER) 		s += sprintf(s,  "U ");
  if (flags & PF_PWT) 		s += sprintf(s,  "PWT ");
  if (flags & PF_PCD) 		s += sprintf(s,  "PCD ");
  if (flags & PF_ACCESSED) 	s += sprintf(s,  "A ");
  if (flags & PF_DIRTY) 	s += sprintf(s,  "D ");
  if (flags & PF_MAP_PAGE) 	s += sprintf(s,  "PS ");
  if (flags & PF_PAT_PTE) 	s += sprintf(s,  "PAT7 ");
  if (flags & PF_PAT12)		s += sprintf(s,  "PAT12 ");
  if (flags & PF_UNEXECUTABLE)	s += sprintf(s,  "XD ");
  
  if (flags & (1llu << 51))	s += sprintf(s,  "51 ");
  
  return str;
}

const char * cmdToString(int action) {
  switch(action) {
    case CMD_READ:  return "Read";
    case CMD_WRITE: return "Write";
    case CMD_RUN:   return "Run";
    case CMD_INV:   return "Inv";
    case CMD_FLAGS: return "Flags";
    case CMD_MAP:   return "Map";
    case CMD_FLUSH: return "Flush";
    case CMD_END:   return "End";
    case CMD_NOOP:  return "Noop";
  }
  
  return "Unknown action";
}

const char * offsetMaskToString(u64 mask) {
  switch(mask) {
    case OFFSET_MASK_4KB: return "[4kb]";
    case OFFSET_MASK_2MB: return "[2mb]";
    case OFFSET_MASK_1GB: return "[1gb]";
  }
  
  return "[?]";
}

const char * addrMaskToString(u64 mask) {
  switch(mask) {
    case ADDR_MASK_64_4KB: return "4kb";
  }
  
  return "?";
}

void printPages(struct test_page *pages, int n) {
  int i;
  for(i=0; i<n; ++i) {
    info("%d %s\n", i, offsetMaskToString (pages[i].offset_mask));
  }
}

void printCommands(struct test_command *cmd, int n) {
  struct test_command *c;
  int i;
  char str[50];
  char str2[150];
  char *s;
  
  for(i=0, c=cmd; i<n; ++i, ++c) {
    s = str2;
    s += sprintf(s, "%s ", cmdToString(c->cmd));
    
    if (PAGE_UNDEFINED == c->page)
      s += sprintf(s, "[UNDEF]");
    else
      s += sprintf(s, "[%d]", c->page);
    
    if (c->cmd == CMD_FLAGS || c->cmd == CMD_MAP) {
      s += sprintf(s, ".%d ", c->pos);
    }
    else {
      s += sprintf(s, " ");
    }
    
    switch(c->cmd) {
      case CMD_FLAGS:
	s += sprintf(s, "%s", flagsToString(c->flags, str));
	
	if (c->uflags) {
	  s += sprintf(s, "!{%s}", flagsToString(c->uflags, str));
	}
	break;
      case CMD_MAP:
	s += sprintf(s, "-> [%d]", c->page2);
	s += sprintf(s, ", mask: %s", addrMaskToString(c->addr_mask));
	break;
      default:
	s += sprintf(s, "%llx", c->vaddr);
    }
    
    s += sprintf(s, "\n");
    info("%s", str2);
  }
}

void print_test (void) {
  info("TEST: %d pages, %d commands\n", TESTER.h.n_pages, TESTER.h.n_commands);
  info("result shift: %ld\n", TESTER.result - TESTER.results);
  info("cmd_ind: %d\ninterrupt_row: %d\n\n", TESTER.cmd_ind, TESTER.interrupt_row);
  
  info("pages:\n");
  printPages (TESTER.pages, TESTER.h.n_pages);
  
  info("\ncommands:\n");
  printCommands (TESTER.commands, TESTER.h.n_commands);
}